--- title: "02-JVM 与运行机制" aliases: - "JVM 与运行机制" created: 2025-12-23 --- # JVM 与运行机制 --- ## 一、JVM、JDK、JRE 的关系 ### **1.1 三者关系图** ```drawio ``` ### **1.2 各组件详解** #### **JVM(Java Virtual Machine)** ```drawio ``` ##### **JVM 的多语言支持:** ```drawio ``` ##### **JVM 的多种实现**: | **JVM 实现** | **开发商** | **特点** | | --- | --- | --- | | **HotSpot** | Oracle/OpenJDK | 最主流,默认 JVM | | **OpenJ9** | IBM/Eclipse | 低内存占用,快速启动 | | **GraalVM** | Oracle | 支持多语言,AOT 编译 | | **Azul Zing** | Azul | 超低延迟 GC | | **Dragonwell** | Alibaba | 针对电商场景优化 | #### **JRE(Java Runtime Environment)** ``` // JRE 核心类库示例 // java.lang - 自动导入,无需 import String str = "Hello"; // java.lang.String Integer num = 100; // java.lang.Integer Thread thread = new Thread(); // java.lang.Thread // java.util - 集合框架 import java.util.ArrayList; import java.util.HashMap; List list = new ArrayList<>(); Map map = new HashMap<>(); // java.io - 输入输出 import java.io.File; import java.io.FileInputStream; File file = new File("test.txt"); // java.net - 网络编程 import java.net.URL; import java.net.HttpURLConnection; URL url = new URL("https://example.com"); ``` #### **JDK(Java Development Kit)** ``` # JDK 目录结构 (JDK 11+) jdk-17/ ├── bin/ # 开发工具 │ ├── java # 运行 Java 程序 │ ├── javac # 编译器 │ ├── javadoc # 文档生成 │ ├── jdb # 调试器 │ ├── jar # 打包工具 │ ├── jlink # 自定义运行时镜像 │ ├── jshell # 交互式 REPL (JDK 9+) │ ├── jconsole # 监控工具 │ ├── jvisualvm # 可视化监控 (需单独下载) │ └── ... ├── conf/ # 配置文件 ├── include/ # C/C++ 头文件 (JNI) ├── jmods/ # 模块文件 (JDK 9+) ├── lib/ # 核心类库 │ ├── modules # 模块化类库 │ └── src.zip # 源码 └── legal/ # 许可证文件 ``` ``` # 常用 JDK 工具命令 # 编译 Java 文件 javac HelloWorld.java # 运行 Java 程序 java HelloWorld # 查看字节码 javap -c HelloWorld.class # 生成文档 javadoc -d docs *.java # 打包 JAR jar cvf app.jar *.class # 交互式 Shell (JDK 9+) jshell # 创建自定义运行时 (JDK 9+) jlink --module-path $JAVA_HOME/jmods \ --add-modules java.base \ --output custom-runtime ``` ### **1.3 重要变化** ```drawio ``` ``` // JDK 9+ 模块化示例 // module-info.java module com.myapp { requires java.base; // 基础模块(默认) requires java.sql; // 数据库模块 requires java.logging; // 日志模块 exports com.myapp.api; // 导出包供其他模块使用 } ``` --- ## 二、字节码详解 ### **2.1 什么是字节码** ```drawio ``` ### **2.2 字节码文件结构** ```drawio ``` ### **2.3 字节码实战分析** ```java // 源代码: HelloWorld.java public class HelloWorld { private int count = 0; public int add(int a, int b) { return a + b; } public static void main(String[] args) { HelloWorld hw = new HelloWorld(); int result = hw.add(1, 2); System.out.println(result); } } ``` **javap反编译:** ``` # 编译 javac HelloWorld.java # 反编译查看字节码 javap -v -p HelloWorld.class ``` **字节码输出分析**: ``` Classfile /path/to/HelloWorld.class Last modified 2024-1-1; size 537 bytes SHA-256 checksum abc123... Compiled from "HelloWorld.java" // ==================== 类信息 ==================== public class HelloWorld minor version: 0 major version: 61 // JDK 17 flags: (0x0021) ACC_PUBLIC, ACC_SUPER this_class: #8 // HelloWorld super_class: #2 // java/lang/Object interfaces: 0, fields: 1, methods: 3, attributes: 1 // ==================== 常量池 ==================== Constant pool: #1 = Methodref #2.#3 // java/lang/Object."":()V #2 = Class #4 // java/lang/Object #3 = NameAndType #5:#6 // "":()V #4 = Utf8 java/lang/Object #5 = Utf8 #6 = Utf8 ()V #7 = Fieldref #8.#9 // HelloWorld.count:I #8 = Class #10 // HelloWorld ... // ==================== 字段定义 ==================== private int count; descriptor: I // I 表示 int 类型 flags: (0x0002) ACC_PRIVATE // ==================== 方法: add ==================== public int add(int, int); descriptor: (II)I // 参数: 2个int, 返回: int flags: (0x0001) ACC_PUBLIC Code: stack=2, locals=3, args_size=3 // 操作数栈深度=2, 局部变量=3 0: iload_1 // 将第1个int参数压入操作数栈 1: iload_2 // 将第2个int参数压入操作数栈 2: iadd // 弹出两个int相加,结果压栈 3: ireturn // 返回栈顶int值 LineNumberTable: line 5: 0 // 源代码第5行对应字节码偏移0 // ==================== 方法: main ==================== public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: (0x0009) ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=3, args_size=1 0: new #8 // 创建 HelloWorld 对象 3: dup // 复制栈顶引用 4: invokespecial #1 // 调用构造方法 7: astore_1 // 存储到局部变量1 8: aload_1 // 加载局部变量1 9: iconst_1 // 常量1压栈 10: iconst_2 // 常量2压栈 11: invokevirtual #11 // 调用 add 方法 14: istore_2 // 存储结果到局部变量2 15: getstatic #12 // 获取 System.out 18: iload_2 // 加载结果 19: invokevirtual #18 // 调用 println 22: return // 返回 ``` ### **2.4 常见字节码指令** ```drawio ``` ### **2.5 字节码执行过程图解** ```drawio ``` --- ## 三、编译与解释并存 ### **3.1 编程语言执行方式分类** ```drawio ``` ### **3.2 Java 程序完整执行流程** ```drawio ``` ### **3.3 为什么说 Java 是"编译与解释并存"** ```java // 一段 Java 代码的执行过程 public class Demo { public static void main(String[] args) { // 循环执行 10000 次 for (int i = 0; i < 10000; i++) { calculate(i); } } public static int calculate(int n) { return n * n + n; } } ``` **执行过程**: ```drawio ``` **结论**: | **阶段** | **方式** | **语言特征** | | --- | --- | --- | | .java → .class | 编译 | 编译型 | | 字节码 → 解释执行 | 解释 | 解释型 | | 热点代码 → 机器码 | 编译 | 编译型 | --- ## 四、JIT 即时编译器 ### **4.1 JIT 工作原理** ```drawio ``` ### **4.2 热点探测机制** ```java // 热点探测示例 public class HotSpotDemo { private static int counter = 0; // 这个方法会被识别为"热点方法" public static int hotMethod(int n) { return n * n; } public static void main(String[] args) { // 大量调用,触发 JIT 编译 for (int i = 0; i < 100_000; i++) { hotMethod(i); } // 此时 hotMethod 已被 JIT 编译为机器码 // 后续调用将直接执行机器码 long start = System.nanoTime(); for (int i = 0; i < 100_000; i++) { hotMethod(i); } long end = System.nanoTime(); System.out.println("执行时间: " + (end - start) + " ns"); } } ``` **热点探测方式**: ```drawio ``` ### **4.3 JIT 优化技术** #### **方法内联 (Method Inlining)** ```java // 优化前 public int calculate(int x) { return square(x) + cube(x); } private int square(int n) { return n * n; } private int cube(int n) { return n * n * n; } // JIT 优化后 (方法内联) public int calculate(int x) { return (x * x) + (x * x * x); // 直接展开,消除方法调用开销 } ``` #### **逃逸分析 (Escape Analysis)** ```java // 逃逸分析示例 public class EscapeAnalysisDemo { public int calculate() { // point 对象未逃逸出方法 Point point = new Point(1, 2); return point.x + point.y; } private class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } } } // JIT 优化后 (标量替换) public int calculate() { // 对象被拆解为标量,直接在栈上分配 int x = 1; int y = 2; return x + y; // 可能进一步优化为 return 3; } ``` ```drawio ``` #### **循环优化** ``` // 循环展开 (Loop Unrolling) // 优化前 for (int i = 0; i < 4; i++) { arr[i] = i * 2; } // JIT 优化后 arr[0] = 0; arr[1] = 2; arr[2] = 4; arr[3] = 6; // 循环剥离 (Loop Peeling) // 优化前 for (int i = 0; i < n; i++) { if (i == 0) { init(); } process(i); } // JIT 优化后 init(); process(0); for (int i = 1; i < n; i++) { process(i); // 循环内不再有条件判断 } ``` ### **4.4 JIT 相关 JVM 参数** ``` # 查看 JIT 编译过程 java -XX:+PrintCompilation Demo # 输出示例: # 时间戳 编译ID 属性 层级 方法名 176 1 n 0 java.lang.Object:: (1 bytes) 178 2 n 0 java.lang.String::hashCode (55 bytes) 180 3 b 3 Demo::hotMethod (6 bytes) # 属性含义: # n = native 方法 # s = synchronized 方法 # b = 阻塞编译 # ! = 有异常处理 # % = OSR 编译 # 常用 JIT 参数 -XX:+TieredCompilation # 启用分层编译(默认开启) -XX:CompileThreshold=10000 # 编译阈值 -XX:+PrintCompilation # 打印编译信息 -XX:+UnlockDiagnosticVMOptions # 解锁诊断选项 -XX:+PrintInlining # 打印内联信息 -XX:+PrintAssembly # 打印汇编代码(需要 hsdis 插件) ``` --- ## 五、AOT 提前编译 ### **5.1 AOT 概述** ```drawio ``` ### **5.2 JIT vs AOT 详细对比** | **指标** | **JIT 即时编译** | **AOT 提前编译** | | --- | --- | --- | | **编译时机** | 运行时 | 构建时 | | **启动速度** | 慢(需预热) | 快 | | **峰值性能** | 高(可做运行时优化) | 中等 | | **内存占用** | 较大 | 较小 | | **打包体积** | 较大 | 较小 | | **动态特性** | 完全支持 | 受限 | | **适用场景** | 长时间运行的服务 | 云原生/Serverless | ### **5.3 为什么不全用 AOT?** ```drawio ``` ```java // 反射示例 - AOT 需要额外配置 public class ReflectionDemo { public static void main(String[] args) throws Exception { // 运行时动态加载类 - AOT 无法预知 Class clazz = Class.forName("com.example.User"); Object obj = clazz.getDeclaredConstructor().newInstance(); // 动态调用方法 Method method = clazz.getMethod("getName"); String name = (String) method.invoke(obj); } } // 使用 GraalVM Native Image 需要提供反射配置文件 // reflect-config.json [ { "name": "com.example.User", "allDeclaredConstructors": true, "allPublicMethods": true } ] ``` ### **5.4 GraalVM 与 Native Image** ```drawio ``` **Native Image 构建示例**: ``` # 安装 GraalVM 和 Native Image sdk install java 21-graalce gu install native-image # 编译 Java 代码为原生可执行文件 native-image -jar myapp.jar myapp # 运行原生镜像 ./myapp # 对比启动时间 # JVM 模式: java -jar myapp.jar → 约 1-3 秒 # Native 模式: ./myapp → 约 10-50 毫秒 ``` **Spring Boot 3 Native 支持**: ``` org.graalvm.buildtools native-maven-plugin ``` ``` # 构建 Spring Boot Native 镜像 mvn -Pnative native:compile # 或使用 Buildpacks mvn spring-boot:build-image -Pnative ``` ### **5.5 AOT 适用场景** ```drawio ``` --- ## 六、Oracle JDK 与 OpenJDK ### **6.1 发展历史** ```drawio ``` ### **6.2 主要区别对比** ```drawio ``` ### **6.3 JDK 发行版生态** ```drawio ``` ### **6.4 如何选择 JDK** ```drawio ``` --- ## 七、总结 ```drawio ```